#include "TechBattleControl.h"
#include "UnitTypes.h"

// Putin engine includes
#include "../shared/time.h"
#include "../shared/shared_types.h"
#include "../engine/unit.h"
#include "../net/NetworkClient.h"

#include "../engine/player.h"
#include "../gfx/RenderEngine.h"
#include "../multimedia/SoundManager/SoundManager/include/SoundManager.h"
#include "../shared/utils.h"

#include <fstream>
#include <sstream>
#include <cmath>

using namespace Putin;
using std::max;

static Vec3f tempSpeed = Vec3f(1.0f,1.0f,1.0f); // just to have smthn to use with the sound..

//--------------------------------------
// BattleControl Implementation Definitions 

TechBattleControl::TechBattleControl(bool mp)
{
	mNetworkMode = mp;
	mCamPos = Vec3f( 100.0f, 450.0f, 300.0f );
	mLookAt = Vec3f( 100.0f, 40.0f, 100.0f );
	mInitedMP = false;
} 

bool TechBattleControl::initialize()
{
	// any last minute inits here..
	bool ret = BattleControl::initialize();
	// TEST, to keep music down when testing.. just remove it if needed
	SoundManager::instance().setMasterVolume(0.3f);
	playMusic();
	SoundManager::instance().setMusicVolume(0.5f);

	

	return ret;
}

bool TechBattleControl::loadResources()
{
	BattleControl::loadResources();

	if( isMultiplay())
	{
		mSettings = new GameSettings();
		mSettings->loadFromFile("netgame.cfg");
		mLocalPlayer = new Putin::Player("Local");
		mLocalPlayer->mId = 0; // to be set by the server
		mNet->connect(mSettings->server);
		RenderEngine::instance().loadMap(mSettings->map+".cfg");
		loadMap("../putinresources/maps/"+mSettings->map+".gbm");
		
		mGame = new Gamestate(mMap,4);
	}
	else
	{

		RenderEngine::instance().loadMap( "fathermotherland.cfg" );
		loadMap("../putinresources/maps/fathermotherland.gbm");
		mGame = new Gamestate(mMap,3);
	}
	preloadStaticObjects();
	//RenderEngine::instance().setSkybox("Putin/RedSky");
	setMusic("music.ogg");
	loadSound("tank_engine.wav");
	loadSound("tank_sharp_turn.wav");
	loadSound("tank_canon.wav");
	loadSound("tank_hit_1.wav");
	loadSound("tank_hit_2.wav");
	loadSound("tank_explode_1.wav");
	loadSound("tank_explode_2.wav");
	loadSound("tank_explode_3.wav");
	return true;
}

// Preloading static objects on obstacles on the map
void TechBattleControl::preloadStaticObjects()
{
	for(int x = 0; x<mMap->getW(); x++)
	{
		for(int y = 0; y < mMap->getH(); y++)
		{
			if( mMap->isObstacle(x,y))
			{
				Vec3f wp = *mAIManager->mapToWorld( &Vec2i(x,y));
				// find y.
				wp.z = mWorldLink->getHeight(wp);
				spawnStatic(new Stone(wp,Vec3f(1.0f,0.0f,0.0f)));
			}
		}
	}
}

bool TechBattleControl::update()
{
	bool ret = true;


	

	/* Code to remove exploded bullets

	for each(std::pair<int,Bullet*> up in mBullets)
	{
		if(up.second->mHasExploded)
			removeBullet(up.second);
	}*/

	
	// Update multiplayer
	if( isMultiplay() )
	{
		mNet->update();

		if( mNet->getState() == NetworkClient::RUNNING && !mInitedMP )
		{
			// First time Multiplay init
			for(int i=0;i<mSettings->numTanks;i++)
			{
				spawnUnit(new Leo(Vec3f(i*75.0f,i*30.0f,35),Vec3f(1.0f,0.0f,0.0f),mLocalPlayer));
			}
			mInitedMP = true;
		}
		ret = BattleControl::update();

	}
	else // NO MP. just some simple one computer testing
	{
		ret = BattleControl::update();

		/*if( this->mFrameCounter16 % 40 == 0 )
		{
			Bullet* m = new MissileX(Vec3f( ), Vec3f( ), 3);
		}*/
	}

	return ret;
}

bool TechBattleControl::renderGraphics()
{
	// Apply camera
	RenderEngine::instance().setCameraPosition(mCamPos);
	RenderEngine::instance().setCameraLookAt(mLookAt);

	
	double ft = Time::instance().getLastFrameTime();
	std::stringstream ss;
	ss.setf(std::ios::fixed,std::ios::floatfield);
	ss.precision(1);
	ss << "FPS " << 1.0 / ft;
	if(!isMultiplay())
		RenderEngine::instance().setTextOverlay(ss.str());

	bool ret = BattleControl::renderGraphics();
	// Show/hide bounding boxes
	for each( RenderableUnit* ru in mRenderables)
	{
		if( mGame->mSelected[atoi(ru->getId().c_str())] == true )
		{
			RenderEngine::instance().showUnitBoundingBox(ru,true);			
		}
		else
		{
			RenderEngine::instance().showUnitBoundingBox(ru,false);
		}
	}

	return ret ;
}

void TechBattleControl::keyDown(char key)
{
	// Only one key is registred in "key", so we have to check manually perhaps
	
	// Camera movement
	const float t = Time::instance().getLastFrameTime();
	const float moveSpeed = 300.0f; // units / sec
	const float C = t*moveSpeed;
	
	
	
	if(key == VK_NEXT)
	{
		mCamPos.y -= C;
		mCamPos.y = max(mCamPos.y, 50.0f);
	}
	if(key == VK_PRIOR)
	{
		mCamPos.y += C;
	}
	if(key == VK_LEFT)
	{
		mCamPos.x -= C;
		mLookAt.x -= C;
	}
	if(key == VK_RIGHT)
	{
		mCamPos.x += C;
		mLookAt.x += C;
	}
	if(key == VK_UP)
	{
		mCamPos.z -= C;
		mLookAt.z -= C;
	}
	if(key == VK_DOWN)
	{
		mCamPos.z += C;
		mLookAt.z += C;
	}
	if(key == 'O')
	{
		mPhycccp->mCollisionsOn = true;
	}
	if(key == 'P')
	{
		mPhycccp->mCollisionsOn = false;
	}

	// Network start
	if( key == 'G' && isMultiplay() && mNet->getState() == NetworkClient::WAITING_GO)
	{
		sendStartMultiplay();
	}
	
	float zeros[]  = {0.0, 0.0, 0.0};

	SoundManager::instance().setListenerProperties(mCamPos.v, zeros, mLookAt.normalized().v);
} 

void TechBattleControl::clicked(int mouse)
{
	const float t = Time::instance().getLastFrameTime();
	const float moveSpeed = 100.0f; // units / sec
	const float C = t*moveSpeed;
	if( mouse == VK_SCROLL )
	{
		mCamPos.y += C;
	}
}

void TechBattleControl::unitClicked(int unitID, Putin::Vec3f &pos, int mouse)
{
	if( mGame->getUnit(unitID) == NULL) // Stone or whatever clicked...
		return; 


	// Left mouse clicked on a unit. Select it.
	if( mouse == VK_LBUTTON )
	{
		mGame->select( unitID );
		Unit* u = mGame->getUnit(unitID);
		playSound("tank_engine.wav",u->getPosition(),tempSpeed, "normal");
	}
	// Right mouse: Initiate attack command
	else if( mouse == VK_RBUTTON)
	{
		// our trops or not?
		if( mGame->getUnit(unitID)->getPlayer()->getControlType() != Player::HUMAN)
		{
			
		}
	}
}

void TechBattleControl::groundClicked(Putin::Vec3f& pos, int mouse)
{
	// Left mouse clicked on a unit. Select it.
	if( mouse == VK_LBUTTON )
	{
		mGame->clearSelection();
	}
	// Right mouse: Initiate attack command
	else if( mouse == VK_RBUTTON)
	{
		// Move command to selected units
		for each( std::pair<int,bool> up in mGame->mSelected)
		{
			if( !up.second ) continue;
			Unit* u = mGame->getUnit(up.first);
			if( isMultiplay()) addUnitMoveCmd(u,pos);
			else u->moveTo(pos);

			// play some sound..
			//playSound("tank_sharp_turn.wav",u->getPosition(),tempSpeed, "normal");
		}
	}

	//BattleControl::groundClicked(pos,mouse);
}

void TechBattleControl::addUnitMoveCmd(Putin::Unit *u, Putin::Vec3f target)
{
	if( !isMultiplay()) return;
	NetworkCommand moveCmd(NC_MOVE,u->getID(),0,target.x,target.y);
	mNet->send(&moveCmd);
}

void TechBattleControl::sendStartMultiplay()
{
	if(!isMultiplay()) return;
	NetworkCommand startCmd(NC_GO,0,0,0,0);
	mNet->send(&startCmd);
}

void TechBattleControl::executeCommand(Putin::NetworkCommand* nc)
{
	switch( nc->command )
	{
		case NC_MOVE:
		{
			int id = (int)nc->id1;
			Putin::Unit* u = mGame->getUnit(id);
			Vec3f v(nc->x,nc->y,0);
			u->moveTo(v);
			break;
		}

		case NC_SPAWN:
		{
			// id2 = tank type. 1 = leo2red, 2 = leo2
			Putin::Unit* u = NULL;
			if( nc->id2 == 1 )
			{
				u = new Leo(Putin::Vec3f(nc->x,nc->y,40),Putin::Vec3f(1.0f,0.0f,0.0f),mLocalPlayer);	
			}
			else
			{
				u = new Leo(Putin::Vec3f(nc->x,nc->y,40),Putin::Vec3f(1.0f,0.0f,0.0f),mLocalPlayer);
			}
			BattleControl::spawnUnit(u);
			break;
		}

		case NC_PLAYER_R:
		{
			mLocalPlayer->mId = nc->id1;
			RenderEngine::instance().setTextOverlay(std::string("Connected with id ") + Putin::Utils::intToString(nc->id1));
			break;
		}
		case NC_PLAYER:
		{
			std::string pname = "Player"+Putin::Utils::intToString(nc->id1);
			Putin::Player* p = new Putin::Player(pname);
			p->mId  = nc->id1;
			RenderEngine::instance().setTextOverlay(pname + " has connected..");
			break;
		}
		default:
			BattleControl::executeCommand(nc);
	}
}

bool TechBattleControl::spawnUnit(Putin::Unit* unit)
{
	if( isMultiplay())
	{
		// just push a command to spawn a unit
		Putin::NetworkCommand spawn(NC_SPAWN,0,1, unit->getPosition().x, unit->getPosition().y);
		mNet->send(&spawn);
		return true;
	}
	else
	{
		return BattleControl::spawnUnit(unit);
	}
}
//-----------------------------------
// Game Setting clazz
void GameSettings::loadFromFile(std::string filename)
{
	// load settings file
	std::ifstream fin(filename.c_str());
	while( !fin.eof())
	{
		char line[256];
		fin.getline(line,256);
		std::string s(line);
		if( s.length() < 1 ) continue;
		std::string id = s.substr(0,s.find_first_of(' '));
		std::string val = s.substr(s.find_first_of(' ')+1,s.npos);
		if( id == "numTanks")
			this->numTanks = atoi(val.c_str());
		else if( id == "mapName")
			this->map = val;
		else if( id == "server")
			this->server = val;
	}
}